package cn.lisens.common.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
 
/**
 * Dao基类实现
 * 
 * @author 孙振国
 * @date 2024年7月17日
 * @param <T>
 * @param <ID>
 */
public abstract class AbstractBaseDao<T, ID extends Serializable> implements BaseDao<T, ID> {
 
    protected SessionFactory sessionFactory;
    
    @Autowired
	private JdbcTemplate jdbcTemplate;
 
    @SuppressWarnings("unchecked")
    private Class<T> clazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    
    public Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }
    
    protected JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
    
    @Override
    public void save(T t) {
        this.getCurrentSession().save(t);
    }
 
    @Override
    public void saveOrUpdate(T t) {
        this.getCurrentSession().saveOrUpdate(t);
    }
    
    @Override
    public void update(T t) {
        this.getCurrentSession().update(t);
    }
 
    @Override
    public boolean contains(T t) {
        return this.getCurrentSession().contains(t);
    }
 
    @Override
    public void delete(T t) {
        this.getCurrentSession().delete(t);
    }
 
    @Override
    public boolean deleteById(ID Id) {
        T t = this.get(Id);
        if (t == null)  return false;
        this.delete(t);
        return true;
    }
 
    @Override
    public void deleteAll(Collection<T> entities) {
        Session session = this.getCurrentSession();
        for (T entity : entities) {
            session.delete(entity);
        }
    }
 
    @Override
    public int excuteHql(String hqlString) {
        return this.getCurrentSession().createQuery(hqlString).executeUpdate();
    }
    
    @Override
    public int excuteHql(String hqlString, Map<String, ?> params) {
    	Query<?> query = this.getCurrentSession().createQuery(hqlString);
        return this.setParameterToQuery(query, params).executeUpdate();
    }
 
    @Override
    public int excuteSql(String sqlString) {
        return this.getCurrentSession().createNativeQuery(sqlString).executeUpdate();
    }
    
    @Override
    public int excuteSql(String sqlString, Map<String, ?> params) {
        return this.setParameterToQuery(this.getCurrentSession().createNativeQuery(sqlString), params).executeUpdate();
    }
    
    @Override
    public T get(ID id) {
        return this.getCurrentSession().get(clazz, id);
    }
 
    @Override
    public T getByHQL(String hqlString, Map<String, ?> params) {
        Query<T> query = this.getCurrentSession().createQuery(hqlString, clazz);
        this.setParameterToQuery(query, params);
        return query.uniqueResult();
    }
    
 
    @Override
    public <V> V getByHQL(String hqlString, Map<String, ?> params, Class<V> theClass) {
        Query<V> query = this.getCurrentSession().createQuery(hqlString, theClass);
        this.setParameterToQuery(query, params);
        return query.uniqueResult();
    }
 
    @Override
    public T getBySQL(String sqlString, Map<String, ?> params) {
        NativeQuery<T> query = this.getCurrentSession().createNativeQuery(sqlString, clazz);
        this.setParameterToQuery(query, params);
        return query.uniqueResult();
    }
    
    @Override
    public <V> V getBySQL(String sqlString, Map<String, ?> params, Class<V> theClass) {
        NativeQuery<V> query = this.getCurrentSession().createNativeQuery(sqlString, theClass);
        this.setParameterToQuery(query, params);
        return query.uniqueResult();
    }
 
    @Override
    public List<T> listByHQL(String hqlString) {
        return this.getCurrentSession().createQuery(hqlString, clazz).list();
    }
    
    @Override
    public <V> List<V> listByHQL(String hqlString, Class<V> theClass) {
        return this.getCurrentSession().createQuery(hqlString, theClass).list();
    }
 
    @Override
    public List<T> listByHQL(String hqlString, Map<String, ?> params) {
        Query<T> query = this.getCurrentSession().createQuery(hqlString, clazz);
        this.setParameterToQuery(query, params);
        return query.list();
    }
    
    @Override
    public <V> List<V> listByHQL(String hqlString, Map<String, ?> params, Class<V> theClass) {
        Query<V> query = this.getCurrentSession().createQuery(hqlString, theClass);
        this.setParameterToQuery(query, params);
        return query.list();
    }
 
    @Override
    public List<T> listByHQL(String hqlString, int firstResult, int maxResult) {
        Query<T> query = this.getCurrentSession().createQuery(hqlString, clazz);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResult);
        return query.list();
    }
    
    @Override
    public <V> List<V> listByHQL(String hqlString, int firstResult, int maxResult, Class<V> theClass) {
        Query<V> query = this.getCurrentSession().createQuery(hqlString, theClass);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResult);
        return query.list();
    }
 
    @Override
    public List<T> listByHQL(String hqlString, Map<String, ?> params, int firstResult,
            int maxResult) {
        Query<T> query = this.getCurrentSession().createQuery(hqlString, clazz);
        this.setParameterToQuery(query, params);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResult);
        return query.list();
    }
    
    @Override
    public <V> List<V> listByHQL(String hqlString, Map<String, ?> params, int firstResult, int maxResult,
            Class<V> theClass) {
        Query<V> query = this.getCurrentSession().createQuery(hqlString, theClass);
        this.setParameterToQuery(query, params);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResult);
        return query.list();
    }
 
    @Override
    public List<T> listBySQL(String sqlString) {
        return this.getCurrentSession().createNativeQuery(sqlString, clazz).list();
    }
    
    @Override
    public <V> List<V> listBySQL(String sqlString, Class<V> theClass) {
        return this.getCurrentSession().createNativeQuery(sqlString, theClass).list();
    }
 
    @Override
    public List<T> listBySQL(String sqlString, Map<String, ?> params) {
        NativeQuery<T> query = this.getCurrentSession().createNativeQuery(sqlString, clazz);
        this.setParameterToQuery(query, params);
        return query.list();
    }
    
    @Override
    public <V> List<V> listBySQL(String sqlString, Map<String, ?> params, Class<V> theClass) {
        NativeQuery<V> query = this.getCurrentSession().createNativeQuery(sqlString, theClass);
        this.setParameterToQuery(query, params);
        return query.list();
    }
    
    public List<T> findAllUseCriteria() {
    	Session session = sessionFactory.getCurrentSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery<T> cq = builder.createQuery(clazz);
		Root<T> root = cq.from(clazz);
		cq.select(root);
		Query<T> query = session.createQuery(cq);
		return query.getResultList();
    }
    
    public List<T> findListUseCriteria(T t, Map<String, ?> params) {
    	Session session = sessionFactory.getCurrentSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery<T> cq = builder.createQuery(clazz);
		Root<T> root = cq.from(clazz);
		cq.select(root);
		
		List<Predicate> predicateList = new ArrayList<>();
		params.forEach((k,v)->{
			predicateList.add(builder.equal(root.get("k"), v));
		});
		Predicate[] predicates = predicateList.toArray(new Predicate[predicateList.size()]);
		cq.where(builder.and(predicates));
		//默认排序规则
		List<Order> orderList = new ArrayList<>();
		orderList.add(builder.desc(root.get("id")));
		Order[] orders = orderList.toArray(new Order[orderList.size()]);
		cq.orderBy(orders);
		
		Query<T> query = session.createQuery(cq);
		return query.getResultList();
    }
 
    @Override
    public void refresh(T t) {
        this.getCurrentSession().refresh(t);
    }
 
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    @Autowired
    protected void setSessionFactory(SessionFactory sessionFactory) {
    	this.sessionFactory = sessionFactory;
    }
}